home *** CD-ROM | disk | FTP | other *** search
Wrap
/** * SQLite object for mozilla == example usage == var db = new SQLite("%profile%/test.sqlite"); var rs = db.rs("SELECT * FROM USERS WHERE id = ?1", 124); if (!db.error){ for (var i=0; i<rs.length; i++){ ...do something with recordset } } else { ...show error message } */ //http://textsnippets.com/posts/show/1030#related Lazarus.SQLite = function(filepath, useSharedCache){ var _this = this; _this.filepath = filepath; //are we allowed multiple connetcions to this database? _this.useSharedCache = (typeof useSharedCache == "undefined") ? true : useSharedCache; //mozIStorageService _this.storageService = Components.classes["@mozilla.org/storage/service;1"].getService(Components.interfaces.mozIStorageService); //database connection _this.conn = null; //any error object will be placed here. _this.lastError = null; //most recent SQL query _this.lastQuery = null; //array of argument to be passed to the sql query _this.lastQueryArgs = null; /** * opens a connection to the specified SQLite database */ _this.connect = function(){ _this.error = null; if (!_this.conn){ try { //allow for files to be relative to the profile directory if (_this.filepath.indexOf("%profile%") == 0){ var dir = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ProfD", Components.interfaces.nsIFile).path; _this.filepath = _this.filepath.replace("%profile%", dir); if (dir.indexOf("/") > -1){ _this.filepath = _this.filepath.replace(/\\/g, "/"); } else { _this.filepath = _this.filepath.replace(/\//g, "\\"); } } var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); file.initWithPath(_this.filepath); if (_this.useSharedCache){ _this.conn = _this.storageService. openDatabase(file); } else { _this.conn = _this.storageService. openUnsharedDatabase(file); } } catch(err){ //add some better info to the error message throw Error("SQLite: failed to open database\n'"+ _this.filepath +"'\n"+ err.message); } } } /** * close the connection to the database (this doesn't appear to work) */ _this.close = function(){ _this.conn = null; } /** * sends an error to the error console * but allows code to contnue */ _this.logError = function(err){ if (typeof err == "string"){ //generate a better error message err = new Error("SQLite Error: "+ err); } //add some more info to the error if (_this.lastQuery){ //WTF: cannot append data to the err.message! //err.message += "\nSQL = '"+ _this.lastQuery.query +"'"; //we'll throw an error now with the needed info Components.utils.reportError("SQLite.lastQuery = '"+ _this.lastQuery.query +"'"); } _this.lastError = err; } /** * Adds some SQL specific info and then throws the error */ _this.throwError = function(err){ _this.logError(err); throw _this.lastError; } /** * return the final formatted query */ _this.getQuery = function(queryObj){ var q = queryObj.query; for (var i=0; i<queryObj.args.length; i++){ q = q.replace(/\?\d/, "'"+ queryObj.args[i].toString().replace(/'/, "\\'") +"'"); } return q; } /** * executes an sql query against the current database */ _this.runQuery = function(queryObj){ //pass a string (+ replacements args) if (typeof queryObj == "string"){ queryObj = _this.queryFromArguments(arguments); } //or an arguments object that contains a string (+ replacements args) else if (typeof queryObj.length == "number"){ queryObj = _this.queryFromArguments(queryObj); } _this.lastQuery = queryObj; _this.lastError = null; _this.connect(); //make copies of the query and args //so we can add them to error messages var defer = false; //use deferred transaction if database is currently busy if (_this.conn.transactionInProgress){ defer = true; _this.conn.beginTransactionAs(_this.conn.TRANSACTION_DEFERRED); } //build the statement try { var statement = _this.conn.createStatement(queryObj.query); if (queryObj.args){ for (var i=0; i<queryObj.args.length; i++){ var arg = queryObj.args[i].toString(); statement.bindUTF8StringParameter(i, arg); } } //SELECT statements use executeStep() //but all others use execute. //no we can use executeStep() even on a NON select statement. // ref: http://developer.mozilla.org/en/docs/Storage var dataset = []; var columns = null; while (statement.executeStep()){ var row = {}; //we need to calculate the types of each column //do not use statement.columnCount in the for loop, fetches the value again and again var cols = statement.columnCount; if (columns === null){ columns = []; for (var i=0; i<cols; i++){ columns[i] = { "type": statement.getTypeOfIndex(i), //0=null, 1=int, 2=float, 3=string, 4=blob "name": statement.getColumnName(i) }; } } //keep a copy of the columns _this.lastQuery.columns = columns; for (var i=0; i<cols; i++){ var val; switch (columns[i].type){ case 0: //null val = null; break; case 1: //int val = statement.getInt64(i); break; case 2: //float val = statement.getDouble(i); break case 3: //string val = statement.getUTF8String(i); break; case 4: //blob default: _this.logError("Unable to handle datatype "+ columns[i].type); val = null; } row[columns[i].name] = val; } dataset.push(row); } } catch(err){ _this.logError(err); } finally { //must make sure statement is reset statement.reset(); } //commit delayed transaction if (defer){ _this.conn.commitTransaction(); } //if there was an error we should throw it now that the statement has been reset if (_this.lastError){ throw _this.lastError; } //otherwise return any dataset return dataset; } /** * convert an arguments object into a query object */ _this.queryFromArguments = function(argObj){ var query = { "query": argObj[0], "args" : [] } for (var i=1; i<argObj.length; i++){ query.args.push(argObj[i]); } return query; } /** * executes a command (INSERT, UPDATE, CREATE, etc..) against the current database */ _this.exe = function(query, arg1 /*, arg2, arg3...*/){ _this.runQuery(arguments); return true; } /** * execute an INSERT statement and return the last_insert_rowid */ _this.insert = function(){ _this.runQuery("BEGIN TRANSACTION"); _this.runQuery(arguments); var id = _this.getInt("SELECT last_insert_rowid()"); _this.runQuery("COMMIT TRANSACTION"); return id; } /** * runs a SELECT statement and returns a recordset * will return an empty array if no matches are found */ _this.rs = function(query, arg1 /*, arg2, arg3...*/){ return _this.runQuery(arguments); } /** * return a single column of results as an array */ _this.getColumn = function(query, arg1 /*, arg2, arg3...*/){ var rs = _this.runQuery(arguments); var column = []; for (var i=0; i<rs.length; i++){ for(var key in rs[i]){ if (typeof column[i] == "undefined"){ column[i] = rs[i][key]; } else { _this.throwError("Too many columns, getColumn expects the dataset to contain 1 and only 1 column"); } } } return column; } /** * return a single row of results as an associate array (js object) */ _this.getRow = function(query, arg1 /*, arg2, arg3...*/){ var rs = _this.runQuery(arguments); return (rs[0]) ? rs[0] : null; } /** * returns a single INTEGER result from a query. * NOTE: the function expects the query to return a single value, if the query returns a more then one row * or more than one column the we will throw an error */ _this.getInt = function(query, arg1 /*, arg2, arg3...*/){ var rs = _this.runQuery(arguments); if (rs.length == 0){ //no results return 0; } else { for(var col in rs[0]){ var val = parseInt(rs[0][col]); return (val === NaN) ? 0 : val; } } //should get here return null; } /** * returns a single STRING result from a query. * NOTE: the function expects the query to return a single value, if the query returns a more then one row * or more than one column the we will throw an error */ _this.getStr = function(query, arg1 /*, arg2, arg3...*/){ var rs = _this.runQuery(arguments); if (rs.length == 0){ //no results return ''; } else { for(var col in rs[0]){ return rs[0][col]; } } //should not get here return null; } /** * return TRUE if table exists in the current database */ _this.tableExists = function(name){ return (_this.getInt("SELECT count(*) FROM sqlite_master WHERE name = ?1", name) > 0) ? true : false; } }